package logic

import (
	"context"
	"time"

	"gitee.com/night-tc/gobige/dblink"
	"gitee.com/night-tc/gobige/dbmanager"
	"gitee.com/night-tc/gobige/global"
	"gitee.com/night-tc/gobige/logger"
	"gitee.com/night-tc/gobige/safecontainer"
	"gitee.com/night-tc/gobige/threads"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type Player struct {
	playerID uint64 //角色ID

	cacheList map[string]dbmanager.IDataBaseModel //缓存起来的数据
	dirtyList map[string]*Data                    //缓存要更新的数据
	dellist   map[string]dbmanager.IDataBaseModel //缓存要删除的数据

	chanList *safecontainer.SafeListT[IMessage] //收要更新的数据
	thgo     *threads.ThreadGo                  //协程管理器
	conndb   *dblink.MysqlAccess                //数据库连接对象
	endtimer *time.Timer                        // 结束定时器
	EndFunc  func(*Player)                      //结束函数

}

func NewPlayer(playerID uint64, conndb *dblink.MysqlAccess, f func(*Player)) *Player {
	result := new(Player)
	result.playerID = playerID
	result.cacheList = make(map[string]dbmanager.IDataBaseModel)
	result.dirtyList = make(map[string]*Data)
	result.dellist = make(map[string]dbmanager.IDataBaseModel)
	result.conndb = conndb
	result.chanList = safecontainer.NewSafeListT[IMessage]()
	result.EndFunc = f
	return result
}

func (this *Player) GetPlayerID() uint64 {
	return this.playerID
}

func (this *Player) Start(thgo *threads.ThreadGo) {
	this.thgo = threads.NewThreadGoByGo(thgo)
	this.endtimer = time.NewTimer(time.Minute * 10)
	this.thgo.Go(this.goRun) //启动协程
}

// 停止协程是同步调用
func (this *Player) Stop() {

	this.thgo.CloseWait() //等待协程结束
	this.handleMsg()
	this.save()
}

func (this *Player) goRun(ctx context.Context) {
	rdutil := dbmanager.NewEntityUtilDBData(this.playerID)
	defer func() {
		this.endtimer.Stop()
		rdutil.DelDBSession(global.GetCfgInst().GetServerID())
	}()
	uprdtime := global.GetSrvInst().GetNowTime().Unix() + 60*60*24
	for {
		select {
		case <-ctx.Done():
			return
		case <-this.chanList.C:
			this.handleMsg()
			this.save()
			// 过一天了就刷新一下时间
			if uprdtime < global.GetSrvInst().GetNowTime().Unix() {
				uprdtime = uprdtime + 60*60*24
				rdutil.Expire()
			}
		case <-this.endtimer.C:
			this.EndFunc(this) //结束函数
			// this.endtimer.Reset(time.Minute * 10) //重置定时器
		}
	}
}

func (this *Player) handleMsg() {
	for {
		msg, err := this.chanList.Pop()
		if err != nil {
			return
		}
		if msg.IsPreSave() {
			this.save()
		}
		msg.Handler(this)
	}
}

func (this *Player) save() {
	if err := this.conndb.Transaction(func(tx *gorm.DB) error {
		for _, md := range this.dirtyList {
			logger.Debugf(logger.LogKey_Mysql, "Save table %s;", md.Model.KeyName())
			// updata = true
			if md.IsPartial {
				err := tx.Unscoped().Select(md.Columns).Save(md.Model).Error
				if err != nil {
					return err
				}
			} else {
				err := tx.Unscoped().Clauses(clause.OnConflict{UpdateAll: true}).Create(md.Model).Error
				if err != nil {
					return err
				}
			}
		}
		for _, md := range this.dellist {
			err := tx.Delete(md).Error
			if err != nil {
				return err
			}

		}
		return nil
	}); err != nil {
		logger.Warnf("PlayerThread Transaction:%v", err)
	} else {
		this.dirtyList = make(map[string]*Data)
		this.dellist = make(map[string]dbmanager.IDataBaseModel)
	}
}

// 添加要更新的数据
func (this *Player) AddMessage(msg IMessage) {
	this.endtimer.Reset(time.Minute * 10) //重置定时器
	this.chanList.Put(msg)
}

// 是否已超时
func (this *Player) IsTimeout() bool {
	if this.endtimer.Stop() {
		this.endtimer.Reset(time.Minute * 10)
		return false
	} else {
		return true
	}
}
